package com.kevin.learn.util;

import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    public static final Integer MillSec2SecRate = 1000;

    private static String[] parsePatterns = {
            "yyyy-MM-dd",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "yyyy-MM",
            "yyyy/MM/dd",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy/MM/dd HH:mm",
            "yyyy/MM",
            "yyyy.MM.dd",
            "yyyy.MM.dd HH:mm:ss",
            "yyyy.MM.dd HH:mm",
            "yyyy.MM"
    };

    private static ThreadLocal<Calendar> calInstance = new ThreadLocal<Calendar>() {

        protected Calendar initialValue() {
            return Calendar.getInstance();
        }
    };

    public static final String DF_yyyy_MM_dd_HHmmss = "yyyy-MM-dd HH:mm:ss";

    public static final String DF_yy_MM_dd_HHmmss = "yy-MM-dd HH:mm:ss";

    public static final String DF_yyyy_MM_dd_HHmm = "yyyy-MM-dd HH:mm";

    public static final String DF_yyyyMMddHHmmss = "yyyyMMddHHmmss";

    public static final String DF_yyyyMMdd = "yyyyMMdd";

    public static final String DF_yyyy_MM_dd = "yyyy-MM-dd";

    public static final String DF_HH_mm_ss = "HH:mm:ss";

    public static final String TIME_PATTERN = DF_yyyy_MM_dd_HHmmss;

    /**
     * 日期型字符串转化为日期 格式
     * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
     * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
     * "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * localDateTime 转 自定义格式string
     */
    public static String formatLocalDateTimeToString(LocalDateTime localDateTime, String format) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
            return localDateTime.format(formatter);
        } catch (DateTimeParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * string 转 LocalDateTime
     */
    public static LocalDateTime stringToLocalDateTime(String dateStr, String format) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
            return LocalDateTime.parse(dateStr, formatter);
        } catch (DateTimeParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 把日期转换成yyyyMMddHH格式
     *
     * @return 长整型
     */
    public static Long toLongYmdh(Date date) {
        if (date != null) {
            return Long.parseLong(new SimpleDateFormat("yyyyMMddHH")
                    .format(date));
        } else {
            return null;
        }
    }

    /**
     * 把日期转换成yyyyMM格式
     *
     * @return 长整型
     */
    public static Long toLongYm(Date date) {
        if (date != null) {
            return Long.parseLong(new SimpleDateFormat("yyyyMM").format(date));
        } else {
            return null;
        }
    }

    /**
     * 把日期转换成yyyyMMdd格式
     *
     * @return 长整型
     */
    public static String toLongYmd(Date date) {
        if (date != null) {
            return new SimpleDateFormat("yyyyMMdd").format(date);
        } else {
            return null;
        }
    }

    /**
     * 把日期加上指定分钟数，返回结果
     */
    public static Date addHour(Date date, int hours) {
        if (date == null) {
            return null;
        }

        Calendar calendar = calInstance.get();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR, hours);
        return calendar.getTime();
    }

    public static Date stringToDate(String date, String format) {
        if (date == null) {
            return null;
        }
        DateFormat dateFormat = null;
        dateFormat = new SimpleDateFormat(format);
        Date day = null;
        try {
            day = dateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return day;
    }

    public static String formatDateToString(Date date, String format) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        return dateFormat.format(date);
    }

    /**
     * 根据日期获取当天结束时间,精确到秒
     */
    public static Date getEndTimeByDate(String date) {
        if (StringUtils.isBlank(date)) {
            return null;
        }
        date += " 23:59:59";
        return stringToDate(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static Date getStartTimeByDate(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    public static Date getEndTimeByDate(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

    public static Date getfirstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(5, 1);
        return cal.getTime();
    }

    public static Date getLastWeekMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getWeekMonday(date));
        cal.add(5, -7);
        cal.set(11, 0);
        cal.set(12, 0);
        cal.set(13, 0);
        cal.set(14, 0);
        return cal.getTime();
    }

    public static Date getWeekMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayWeek = cal.get(7);
        if (1 == dayWeek) {
            cal.add(5, -1);
        }

        cal.setFirstDayOfWeek(2);
        int day = cal.get(7);
        cal.add(5, cal.getFirstDayOfWeek() - day);
        cal.set(11, 0);
        cal.set(12, 0);
        cal.set(13, 0);
        cal.set(14, 0);
        return cal.getTime();
    }

    public static Date getCurrentYearStartTime() {
        Calendar cal = Calendar.getInstance();
        cal.add(2, -cal.get(2));
        cal.set(5, 1);
        cal.set(11, 0);
        cal.set(13, 0);
        cal.set(12, 0);
        return cal.getTime();
    }

    public static Date getCurrentYearEndTime() {
        Calendar cal = Calendar.getInstance();
        cal.add(2, -cal.get(2) + 11);
        cal.set(5, cal.getActualMaximum(5));
        cal.set(11, 23);
        cal.set(13, 59);
        cal.set(12, 59);
        return cal.getTime();
    }

    /**
     * 返回两个日期之间的天数差
     */
    public static Integer diffDay(Date d1, Date d2) {
        if (d1 == null || d2 == null) {
            return null;
        }

        long diff = TimeUnit.MILLISECONDS.toDays(d1.getTime() - d2.getTime());
        return (int) diff;
    }

    /**
     * 比较两个时间相差多少秒
     */
    public static long diffSeconds(Date d1, Date d2) {
        return TimeUnit.SECONDS.toSeconds(d2.getTime() - d1.getTime());
    }

    /**
     * 比较两个时间相差多少分钟
     */
    public static long getDiffMinutes(Date d1, Date d2) {
        return TimeUnit.MINUTES.toMinutes(d2.getTime() - d1.getTime());
    }

    /**
     * 比较两个时间相差多少天
     */
    public static long getDiffDay(Date d1, Date d2) {
        return TimeUnit.MILLISECONDS.toDays(d1.getTime() - d2.getTime());
    }

    /**
     * 返回传入时间月份的最后一天
     */
    public static Date lastDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int value = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        cal.set(Calendar.DAY_OF_MONTH, value);
        return cal.getTime();
    }

    /**
     * 返回传入时间月份的第一天
     */
    public static Date firstDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int value = cal.getActualMinimum(Calendar.DAY_OF_MONTH);
        cal.set(Calendar.DAY_OF_MONTH, value);
        return cal.getTime();
    }

    /**
     * 得到日期时间字符串，转换格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String formatDateTime(Date date) {
        return formatDateToString(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到当前时间字符串 格式（HH:mm:ss）
     */
    public static String getTime() {
        return formatDateToString(new Date(), "HH:mm:ss");
    }

    /**
     * 得到当前日期和时间字符串 格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String getDateTime() {
        return formatDateToString(new Date(), "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到当前年份字符串 格式（yyyy）
     */
    public static String getYear() {
        return formatDateToString(new Date(), "yyyy");
    }

    /**
     * 得到当前月份字符串 格式（MM）
     */
    public static String getMonth() {
        return formatDateToString(new Date(), "MM");
    }

    /**
     * 得到当天字符串 格式（dd）
     */
    public static String getDay() {
        return formatDateToString(new Date(), "dd");
    }

    /**
     * 得到当前星期字符串 格式（E）星期几
     */
    public static String getWeek() {
        return formatDateToString(new Date(), "E");
    }

    /**
     * 获得小时数
     */
    public static Integer getHour(Date date) {
        String t = formatDateToString(date, DF_HH_mm_ss);
        return Integer.valueOf(t.substring(0, 2));
    }

    /**
     * 获得相隔自然天数
     */
    public static Integer getDiffNatualDay(Date d1, Date d2) {
        Date dd1 = stringToDate(formatDateToString(d1, DF_yyyy_MM_dd), DF_yyyy_MM_dd);
        Date dd2 = stringToDate(formatDateToString(d2, DF_yyyy_MM_dd), DF_yyyy_MM_dd);
        return diffDay(dd1, dd2);
    }
}
